/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package protect03;

import java.io.*;
import java.math.BigInteger;
import java.util.Random;
import protect02.EuclidExtended;

/**
 *
 * @author reshetnikovk
 */
public class Protect03 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, FileNotFoundException, ClassNotFoundException {
        test05();
    }
    
    public static void test05() throws FileNotFoundException, IOException, ClassNotFoundException {
        
        //База разрядности p
        BigInteger pBase = BigInteger.TEN.pow(100);
        
        //База разрядности g x
        BigInteger gxBase = BigInteger.TEN.pow(50);
        
        File origin = new File("origin");
        File encoded = new File("encoded");
        File decoded = new File("decoded");
        
        File open = new File("open");
        File close = new File("close");
        
        FileInputStream fis = new FileInputStream(origin);
        
        FileOutputStream fos = new FileOutputStream(encoded);
        
        KeyHolder kh = ElGamal.generateKeys(pBase, gxBase);
        
        ElGamal.encode(kh.getOpenKey(), fis, fos);
        
        fis.close();
        fos.close();
        
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(open));
        oos.writeObject(kh.getOpenKey());
        oos.close();
        
        oos = new ObjectOutputStream(new FileOutputStream(close));
        oos.writeObject(kh.getCloseKey());
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(open));
        OpenKey ok = (OpenKey) ois.readObject();
        ois.close();
        
        ois = new ObjectInputStream(new FileInputStream(close));
        CloseKey ck = (CloseKey) ois.readObject();
        ois.close();
        
        fis = new FileInputStream(encoded);
        fos = new FileOutputStream(decoded);
        
        ElGamal.decode(ok, ck, fis, fos);
        
        fis.close();
        fos.close();
    }
    
    public static void test04() throws IOException {
        
        //База разрядности p
        BigInteger pBase = BigInteger.TEN.pow(19);
        
        //База разрядности g x
        BigInteger gxBase = BigInteger.TEN.pow(11);
        
        KeyHolder kh = ElGamal.generateKeys(pBase, gxBase);
        
        String msg = "Раз Два Три Четыре Пять One Two Three Four Five";
        /*
        String msg = "Раз два три четыре пять,\n"
                + "Вышел зайчик погулять.\n"
                + "Вдруг охотник выбегает,\n"
                + "Прямо в зайчика стреляет!\n"
                + "Пиф-паф, ой-ой-ой!\n"
                + "Умират зайчик мой!";
        /**/
        
        ByteArrayInputStream bais = new ByteArrayInputStream(msg.getBytes());
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        
        ElGamal.encode(kh.getOpenKey(), bais, baos);
        
        bais.close();
        
        bais = new ByteArrayInputStream(baos.toByteArray());
        
        baos.reset();
        
        ElGamal.decode(kh.getOpenKey(), kh.getCloseKey(), bais, baos);
        
        bais.close();
        
        String MSG = new String(baos.toByteArray());
        
        baos.close();
        
        println(MSG);
    }
    
    public static void test03() throws IOException {
        // TODO code application logic here
        
        Random rnd = new Random();
        
        //База разрядности p
        BigInteger pBase = BigInteger.TEN.pow(19);
        
        //База разрядности g x
        BigInteger gxBase = BigInteger.TEN.pow(11);
        
        //зашифроуем это
        //BigInteger m = BigInteger.valueOf(12);
        //BigInteger m = new BigInteger(pBase.bitLength() - 1, rnd);
        //println("m = %s", m);
        String msg = "Раз Два Три Четыре One Two Three Four";
        /*
        String msg = "Раз два три четыре пять,\n"
                + "Вышел зайчик погулять.\n"
                + "Вдруг охотник выбегает,\n"
                + "Прямо в зайчика стреляет!\n"
                + "Пиф-паф, ой-ой-ой!\n"
                + "Умират зайчик мой!";
        /**/
        //Сначала берется просто число p
        //BigInteger p = BigInteger.valueOf(13);
        BigInteger p = new BigInteger(pBase.bitLength(), rnd).add(pBase).nextProbablePrime();
        println("p = %s", p);
        
        //И случайное просто число g, g < p
        //BigInteger g = BigInteger.valueOf(2);
        BigInteger g = new BigInteger(gxBase.bitLength(), rnd).add(gxBase).nextProbablePrime();
        println("g = %s", g);
        
        //И случайное просто число x, x < p
        //BigInteger x = BigInteger.valueOf(7);
        BigInteger x = new BigInteger(gxBase.bitLength(), rnd).add(gxBase).nextProbablePrime();
        println("x = %s", x);
        
        //Затем вычислим y
        BigInteger y = g.modPow(x, p); //Math.round(Math.pow(g, x)) % p;
        println(String.format("y = %s", y));
        
        //y, g, p - это общедоступные ключи
        //x - секретный ключ
        
        //шифруем
        byte[] zeroBuf = new byte[p.toByteArray().length];
        byte[] readBuf = new byte[zeroBuf.length - 1];
        ByteArrayInputStream bais = new ByteArrayInputStream(msg.getBytes());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int readed = 0;
        while (bais.available() > 0) {
            readed = bais.read(readBuf, 1, readBuf.length - 1);
            println("readed = %d", readed);
            
            BigInteger m = new BigInteger(readBuf);
            println("m = %s", m);
                  
            //Для шифрования выбирается случайное число k, взаимнопростое с (p - 1)
            //то есть НОД(k, (p-1)) = 1
            //BigInteger k = BigInteger.valueOf(7);

            BigInteger ps = p.subtract(BigInteger.ONE);       
            EuclidExtended ee = EuclidExtended.generateDualPrimary(ps.bitLength(), ps);
            BigInteger k = ee.b;
            println("k = %s", k);
        
            //Шифрованием превращаем M как пару a, b
            BigInteger a = g.modPow(k, p); //Math.round(Math.pow(g, k)) % p;
            byte[] abuf = a.toByteArray();
            println(String.format("a = %s", a));

            BigInteger b = y.modPow(k, p).multiply(m).mod(p); //Math.round(Math.pow(y, k) * m) % p;
            byte[] bbuf = b.toByteArray();
            println(String.format("b = %s", b));
            
            baos.write(zeroBuf, 0, zeroBuf.length - abuf.length);
            baos.write(abuf);
            
            baos.write(zeroBuf, 0, zeroBuf.length - bbuf.length);
            baos.write(bbuf);
            
            //readed = bais.read(readBuf, 1, readBuf.length - 1);
            //println("readed = %d", readed);
        }
        
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeInt(readed);
        println("readed attached %d", readed);
        
        byte[] data = baos.toByteArray();
        dos.close();
        baos.close();
        
        //Расшифровываем
        BigInteger pssx = p.subtract(BigInteger.ONE).subtract(x);
        
        bais = new ByteArrayInputStream(data);
        baos = new ByteArrayOutputStream();
        byte[] abuf = new byte[zeroBuf.length];
        byte[] bbuf = new byte[zeroBuf.length];
        bais.read(abuf);
        bais.read(bbuf);
        while (bais.available() >= zeroBuf.length) {
            BigInteger a = new BigInteger(abuf);
            BigInteger b = new BigInteger(bbuf);
            
            BigInteger M = a.modPow(pssx, p).multiply(b).mod(p); //(long) (b * (Math.pow(a, p - 1 - x)) % p);
            byte[] mBuf = M.toByteArray();
            println(String.format("M = %d", M));
            
            int zeros = mBuf.length >= readBuf.length ? 0 : readBuf.length - 1 - mBuf.length;
            println("zeros = %d", zeros);
            baos.write(zeroBuf, 0, zeros);
            int from = mBuf.length == readBuf.length ? 1 : 0;
            baos.write(mBuf, from, mBuf.length - from);
            
            bais.read(abuf);
            bais.read(bbuf);
        }
        if (bais.available() > 0) {
            DataInputStream dis = new DataInputStream(bais);
            int left = dis.readInt();
            println("left = %d", left);
            
            BigInteger a = new BigInteger(abuf);
            BigInteger b = new BigInteger(bbuf);
            
            BigInteger M = a.modPow(pssx, p).multiply(b).mod(p); //(long) (b * (Math.pow(a, p - 1 - x)) % p);
            byte[] mBuf = M.toByteArray();
            println(String.format("M = %d length = %d", M, mBuf.length));
            
            int zeros = mBuf.length >= readBuf.length ? 0 : readBuf.length - 1 - mBuf.length;
            println("zeros = %d", zeros);
            baos.write(zeroBuf, 0, zeros);
            
            int from = mBuf.length == readBuf.length ? 1 : 0;
            println("left = %d", left);
            baos.write(mBuf, from, left);
            
            dis.close();
        }
        bais.close();
        
        String MSG = new String(baos.toByteArray());
        baos.close();
        
        println(MSG);
        
    }
    
    public static void test02() {
        // TODO code application logic here
        
        Random rnd = new Random();
        
        //База разрядности p
        BigInteger pBase = BigInteger.TEN.pow(19);
        
        //База разрядности g x
        BigInteger gxBase = BigInteger.TEN.pow(11);
        
        //зашифроуем это
        //BigInteger m = BigInteger.valueOf(12);
        BigInteger m = new BigInteger(pBase.bitLength() - 1, rnd);
        println("m = %s", m);
        
        //Сначала берется просто число p
        //BigInteger p = BigInteger.valueOf(13);
        BigInteger p = new BigInteger(pBase.bitLength(), rnd).add(pBase).nextProbablePrime();
        println("p = %s", p);
        
        //И случайное просто число g, g < p
        //BigInteger g = BigInteger.valueOf(2);
        BigInteger g = new BigInteger(gxBase.bitLength(), rnd).add(gxBase).nextProbablePrime();
        println("g = %s", g);
        
        //И случайное просто число x, x < p
        //BigInteger x = BigInteger.valueOf(7);
        BigInteger x = new BigInteger(gxBase.bitLength(), rnd).add(gxBase).nextProbablePrime();
        println("x = %s", x);
        
        //Затем вычислим y
        BigInteger y = g.modPow(x, p); //Math.round(Math.pow(g, x)) % p;
        println(String.format("y = %s", y));
        
        //y, g, p - это общедоступные ключи
        //x - секретный ключ
        
        //Для шифрования выбирается случайное число k, взаимнопростое с (p - 1)
        //то есть НОД(k, (p-1)) = 1
        //BigInteger k = BigInteger.valueOf(7);
        
        BigInteger ps = p.subtract(BigInteger.ONE);       
        EuclidExtended ee = EuclidExtended.generateDualPrimary(ps.bitLength(), ps);
        BigInteger k = ee.b;
        println("k = %s", k);
        
        //Шифрованием превращаем M как пару a, b
        BigInteger a = g.modPow(k, p); //Math.round(Math.pow(g, k)) % p;
        println(String.format("a = %s", a));
        
        BigInteger b = y.modPow(k, p).multiply(m).mod(p); //Math.round(Math.pow(y, k) * m) % p;
        println(String.format("b = %s", b));
        
        //Расшифровываем
        BigInteger pssx = p.subtract(BigInteger.ONE).subtract(x);
        
        BigInteger M = a.modPow(pssx, p).multiply(b).mod(p); //(long) (b * (Math.pow(a, p - 1 - x)) % p);
        println(String.format("M = %d", M));
    }
    
    public static void test01() {
        // TODO code application logic here
        
        //зашифроуем это
        BigInteger m = BigInteger.valueOf(12);
        println("m = %s", m);
        
        //Сначала берется просто число p
        BigInteger p = BigInteger.valueOf(13);
        println("p = %s", p);
        
        //И случайное просто число g, g < p
        BigInteger g = BigInteger.valueOf(2);
        println("g = %s", g);
        
        //И случайное просто число x, x < p
        BigInteger x = BigInteger.valueOf(7);
        println("x = %s", x);
        
        //Затем вычислим y
        BigInteger y = g.modPow(x, p); //Math.round(Math.pow(g, x)) % p;
        println(String.format("y = %s", y));
        
        //y, g, p - это общедоступные ключи
        //x - секретный ключ
        
        //Для шифрования выбирается случайное число k, взаимнопростое с (p - 1)
        //то есть НОД(k, (p-1)) = 1
        BigInteger k = BigInteger.valueOf(7);
        println("k = %s", k);
        
        //Шифрованием превращаем M как пару a, b
        BigInteger a = g.modPow(k, p); //Math.round(Math.pow(g, k)) % p;
        println(String.format("a = %s", a));
        
        BigInteger b = y.modPow(k, p).multiply(m).mod(p); //Math.round(Math.pow(y, k) * m) % p;
        println(String.format("b = %s", b));
        
        //Расшифровываем
        BigInteger pssx = p.subtract(BigInteger.ONE).subtract(x);
        
        BigInteger M = a.modPow(pssx, p).multiply(b).mod(p); //(long) (b * (Math.pow(a, p - 1 - x)) % p);
        println(String.format("M = %d", M));
    }
    
    public static void test00() {
        // TODO code application logic here
        
        //зашифроуем это
        long m = 12;
        println("m = %s", m);
        
        //Сначала берется просто число p
        long p = 13;
        println("p = %s", p);
        
        //И случайное просто число g, g < p
        long g = 2;
        println("g = %s", g);
        
        //И случайное просто число x, x < p
        long x = 7;
        println("x = %s", x);
        
        //Затем вычислим y
        long y = Math.round(Math.pow(g, x)) % p;
        println(String.format("y = %s", y));
        
        //y, g, p - это общедоступные ключи
        //x - секретный ключ
        
        //Для шифрования выбирается случайное число k, взаимнопростое с (p - 1)
        //то есть НОД(k, (p-1)) = 1
        long k = 7;
        println("k = %s", k);
        
        //Шифрованием превращаем M как пару a, b
        long a = Math.round(Math.pow(g, k)) % p;
        println(String.format("a = %s", a));
        
        long b = Math.round(Math.pow(y, k) * m) % p;
        println(String.format("b = %s", b));
        
        //Расшифровываем
        long M = (b * (Math.round(Math.pow(a, p - 1 - x))) % p);
        println(String.format("M = %d", M));
    }
    
    public static void println(Object o) {
        System.out.println(o);
    }
    
    public static void println(String format, Object o) {
        System.out.println(String.format(format, o));
    }
}
